package com.peress.mycrew.rowing.data;

import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;

import com.peress.mycrew.rowing.data.entries.Acceleration;

public final class DataGatherer implements LocationListener, SensorEventListener {

    public static final int ACCELERATION_SENSOR_SAMPLE_RATE_MICROS = 100000; // 1/10th of a second.

    public static final int MONITOR_BOAT_POSITION = 0x00000001;
    public static final int MONITOR_BOAT_SET = 0x00000002;
    public static final int MONITOR_STROKES = 0x00000004;

    public static final int MONITOR_ALL = MONITOR_BOAT_POSITION | MONITOR_BOAT_SET
            | MONITOR_STROKES;

    private boolean mIsGathering;
    private PieceDataListener mDataListener;

    private final RecordedPiece mRecordedPiece;
    private int mGatherDistance;
    private long mGatherDuration;

    private final LocationManager mLocationManager;
    private final SensorManager mSensorManager;
    private final Sensor mGravitySensor;
    private final Sensor mLinearAccelerationSensor;

    public DataGatherer(LocationManager locationManager, SensorManager sensorManager) {
        mLocationManager = locationManager;
        mSensorManager = sensorManager;
        mGravitySensor = mSensorManager.getDefaultSensor(Sensor.TYPE_GRAVITY);
        mLinearAccelerationSensor =
                mSensorManager.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION);
        mGatherDistance = Integer.MAX_VALUE;
        mGatherDuration = Long.MAX_VALUE;
        mIsGathering = false;
        mRecordedPiece = new RecordedPiece();
    }

    public void setPieceDataListener(PieceDataListener listener) {
        mDataListener = listener;
        mRecordedPiece.setPieceDataListener(listener);
    }

    public void unsetPieceDataListener() {
        mDataListener = null;
        mRecordedPiece.unsetPieceDataListener();
    }

    public void startDataGatheringDistancePiece(int gatherFlags, int distance) {
        startDataGathering(gatherFlags);
        mGatherDistance = distance;
        mGatherDuration = Long.MAX_VALUE;
    }

    public void startDataGatheringTimedPiece(int gatherFlags, long duration) {
        startDataGathering(gatherFlags);
        mGatherDistance = Integer.MAX_VALUE;
        mGatherDuration = duration;
    }

    public void startDataGathering(int gatherFlags) {
        mRecordedPiece.clear();
        mGatherDistance = Integer.MAX_VALUE;
        mGatherDuration = Long.MAX_VALUE;
        if ((gatherFlags & MONITOR_BOAT_POSITION) != 0) {
            mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, this);
        }
        if ((gatherFlags & MONITOR_BOAT_SET) != 0) {
            mSensorManager.registerListener(this, mGravitySensor,
                    ACCELERATION_SENSOR_SAMPLE_RATE_MICROS);
        }
        if ((gatherFlags & MONITOR_STROKES) != 0) {
            mSensorManager.registerListener(this, mLinearAccelerationSensor,
                    ACCELERATION_SENSOR_SAMPLE_RATE_MICROS);
        }
        mIsGathering = true;
        if (mDataListener != null) {
            mDataListener.onGatheringStatusChanged(true /* isGathering */);
        }
    }

    public void stopDataGathering() {
        mLocationManager.removeUpdates(this);
        mSensorManager.unregisterListener(this);
        mIsGathering = false;
        if (mDataListener != null) {
            mDataListener.onGatheringStatusChanged(false /* isGathering */);
        }
    }

    public boolean isGatheringData() {
        return mIsGathering;
    }

    public void testStopGathering() {
        if (mGatherDistance <= mRecordedPiece.getTotalDistance()
                || mGatherDuration <= mRecordedPiece.getTotalTimeMillis()) {
            stopDataGathering();
        }
    }

    @Override
    public void onLocationChanged(Location location) {
        mRecordedPiece.addLocation(location);
        testStopGathering();
    }

    @Override
    public void onStatusChanged(String provider, int status, Bundle extras) {}

    @Override
    public void onProviderEnabled(String provider) {}

    @Override
    public void onProviderDisabled(String provider) {}

    @Override
    public void onSensorChanged(SensorEvent sensorEvent) {
        if (sensorEvent.sensor.equals(mGravitySensor)) {
            mRecordedPiece.addGravityData(
                    new Acceleration(sensorEvent.values, sensorEvent.timestamp));
        } else if (sensorEvent.sensor.equals(mLinearAccelerationSensor)) {
            mRecordedPiece.addAcceleration(
                    new Acceleration(sensorEvent.values, sensorEvent.timestamp));
        }
    }

    @Override
    public void onAccuracyChanged(Sensor sensor, int i) {

    }
}
